package com.leilei;

import cn.hutool.core.util.ObjectUtil;
import cn.hutool.json.JSONUtil;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicLong;

/**
 * @author shuiyun
 * @program collectDemo
 * @description 自定义双向链表
 * @date 2023-02-23 16:06
 **/
public class LinkList<T> {

    private final AtomicLong size;
    private Node<T> root;

    /**
     * 无参构造器
     */
    public LinkList() {
        size = new AtomicLong(0);
        this.root = null;
    }

    /**
     * 有参构造器
     */
    public LinkList(T t) {
        size = new AtomicLong(0);
        Node<T> node = new Node<>(t);
        node.setNext(node);
        node.setPre(node);
        this.root = node;
    }

    public void init(List<T> list) {
        list.forEach(this::add);
    }

    public long getSize() {
        if (null == root) {
            return 0;
        }
        return size.get();
    }

    /**
     * 返回头结点
     *
     * @author shuiyun
     * @date 2023-02-23 16:16
     */
    public Node<T> getFirst() {
        return root;
    }

    /**
     * 返回尾节点
     *
     * @author shuiyun
     * @date 2023-02-23 16:17
     */
    public Node<T> getLast() {
        return root.getPre();
    }

    public void add(T t) {
        if (size.get() == 0L) {
            Node<T> node = new Node<>(t);
            node.setNext(node);
            node.setPre(node);
            this.root = node;
            this.size.incrementAndGet();
            return;
        }
        add(root.getPre(), t);
    }

    /**
     * 指定节点后插入数据
     *
     * @param node 指定节点
     * @param t    添加的节点元素
     * @author shuiyun
     * @date 2023-02-23 16:24
     */
    public void add(Node<T> node, T t) {
        size.incrementAndGet();
        Node<T> temp = new Node<>(node, t, node.getNext());
        // 修改原有根节点和上一个节点的
        node.getNext().setPre(temp);
        node.setNext(temp);
    }

    /**
     * 指定元素后插入数据
     *
     * @param tag 定位点
     * @param t   待插入元素
     * @author shuiyun
     * @date 2023-02-24 10:04
     */
    public void add(T tag, T t) {
        Node<T> temp = get(tag);
        if (ObjectUtil.isNull(temp)) {
            throw new RuntimeException("未找到对应元素:" + JSONUtil.toJsonStr(tag));
        }
        add(temp, t);

    }


    /**
     * 删除节点
     *
     * @param node 待删除的节点
     * @author shuiyun
     * @date 2023-02-23 16:30
     */
    public void del(Node<T> node) {
        // 如果删除的是根节点
        if (node.equals(root)) {
            if (root.getNext().equals(root)) {
                root = null;
            } else {
                this.root = node.getNext();
            }
        }
        size.decrementAndGet();
        Node<T> pre = node.getPre();
        Node<T> next = node.getNext();

        pre.setNext(next);
        next.setPre(pre);
        node.clear();
    }

    /**
     * 查找节点
     *
     * @param t 节点元素
     * @author shuiyun
     * @date 2023-02-23 16:47
     */
    public Node<T> get(T t) {
        Node<T> node = getFirst();
        while (!node.getNext().equals(root) && !node.getData().equals(t)) {
            node = node.getNext();
        }
        if (node.getData().equals(t)) {
            return node;
        } else {
            return null;
        }
    }

    public List<T> getList() {
        List<T> list = new ArrayList<>();
        if (null == root) {
            return list;
        }
        Node<T> node = root;
        list.add(node.getData());
        while (!node.getNext().equals(root)) {
            node = node.getNext();
            list.add(node.getData());
        }
        return list;
    }

    public void del(T t) {

        Node<T> node = get(t);
        if (ObjectUtil.isNotNull(node)) {
            del(node);
        }
    }


    @Override
    public String toString() {

        StringBuilder builder = new StringBuilder("[");
        Node<T> node = this.root;
        if (null == node) {
            return "[]";
        }

        builder.append(node.getData().toString());
        while (!node.getNext().equals(root)) {
            node = node.getNext();
            builder.append(", ").append(node.getData().toString());
        }
        builder.append("]");

        return builder.toString();
    }
}
